package Cammy::ImageProcessor;
use strict;
use warnings;
# VERSION
# ABSTRACT: Object oriented module for processing using perlmagick

use Image::Magick;

=head1 SYNOPSIS

    ...

=head1 DESCRIPTION

...

=head1 FUNCTIONS

=head2 new

ImageProcessor
    Attributes given as args for constructor:
       input_file
       output_file
       blur_radius
       blur_sigma
       shape_spec
   Attributes:
       input_file : Image filename read from
       output_file: Image filename written to
       image       : Image::Magick object for image manipulation
       blur_radius: Used for blurring see http://www.imagemagick.org/Usage/blur/
       blur_sigma : Used for blurring see http://www.imagemagick.org/Usage/blur/
       shape_spec : The geometry used to make the blur mask

=cut

sub new {
    my $class = shift;
    my %args  = @_;
    my $self  = {
        output_file => $args{output_file},
        thumb_file  => $args{thumb_file},
        image       => Image::Magick->new( magick => 'jpg' ),
        blur_radius => $args{blur_radius},
        blur_sigma  => $args{blur_sigma},
        shape_spec  => $args{shape_spec},
    };
    if ( $args{image_blob} ) {
        my $e = $self->{image}->BlobToImage($args{image_blob});
        if ($e) {
            warn "Filed to read any images from the binary blob: $e";
            return;
        }
    }
    else {
        unless ( $self->{image}->Read($args{input_file}) ){
            warn "Failed to read any images from: $args{input_file}";
            return;
        }
    }
    return bless $self, $class;
}

=head2 crop($geometry)

Just a wrapper method for imagemagick's crop, error checks the imput.
Having it here keeps image magick functions out of the main script.

=cut

sub crop {
    my $self = shift;
    my $geo  = shift;
    if ($geo =~ m/^[0-9]+X|x[0-9]+\+[0-9]+\+[0-9]+$/){
        $self->{image}->Crop( geometry => $geo );
    }
    else {
        die "Invalid geometry format $geo";
    }
}

=head2 blur_mask

Blurs the shapes described in shape file using a mask compose

=cut

sub blur_mask {
    my $self = shift;

    # The blurred version of the image
    my $clone = $self->{image}->Clone;
    my ($width, $height) = $self->{image}->Get('width', 'height');

    # The mask layer. White will be used to tell compose where to overlay the blurred image
    my $mask = Image::Magick->new( size => $width.'x'.$height );
    my $err = $mask->Read('xc:black');
    die "$err" if "$err";
    undef $err;

    if ($self->{shape_spec} =~ m/(\S+)((?:[0-9]|,| )*)/) {
        $err = $mask->Draw( fill => 'white', primitive => $1, points => $2 );
        die "$err" if "$err";
        undef $err;
    }
    $err = $clone->Blur(
        radius  => $self->{blur_radius},
        sigma   => $self->{blur_sigma},
        channel => 'All',
    );
    die "$err" if "$err";
    undef $err;

    $err = $self->{image}->Composite( image => $clone, compose => 'Over', mask => $mask );
    die "$err" if "$err";
    undef $err;
}

=head2 label($text, $red_text)

Adds text to a white on black bar centred at the top of the image.
Adds text in read (suitable for warnings) at the bottom if provided.

=cut

sub label {
    my ($self, $text, $warning) = @_;
    $self->{image}->Splice(
        geometry    => '0x1',
        background  => 'black',
        gravity     => 'South',
    );
    $self->{image}->Annotate(
        fill        => 'white',
        pointsize   => 18,
        antialias   => 1,
        text        => $text,
        gravity     => 'North',
    ) if $text;
    $self->{image}->Annotate(
        fill        => 'red',
        undercolor  => '#000000f0',
        text        => $warning,
        gravity     => 'South',
    ) if $warning;
}

=head2 add_banner($logo)

Adds a logo in the corner above the weather label. Provide
the logo's filename.

=cut

sub add_banner {
    my ($self, $logo_filename) = @_;
    my $logo = Image::Magick->new;
    $logo->Read($logo_filename) != 0
        or die "Failed to read logo file '$logo_filename': $!";
    $self->{image}->Composite( gravity => 'NorthEast', image => $logo );
}

=head2 thumb($geometry)

Create a thumbnail of the current image with the given geometry.
Save it with L</write>.

=cut

sub thumb {
    my $self = shift;
    my $geo  = shift;
    die 'thumb_file not set' unless $self->{thumb_file};

    $self->{thumb} = $self->{image}->Clone;
    $self->{thumb}->Thumbnail($geo);
}

=head2 write

Saves the image file
If a filename is given it is written there instead of the filename held by the object

If you have created a thumbnail with C<< L<thumb|/thumb($geometry)> >>, then also save that file.

=cut

sub write {
    my $self = shift;
    my $file = shift;

    $self->{image}->Write($file || $self->{output_file});
    $self->{thumb}->Write($self->{thumb_file})
        if $self->{thumb} and $self->{thumb_file};
}

1;
